--
-- DESCRIPTION
--
-- @COMPANY **
-- @AUTHOR **
-- @DATE ${date} ${time}
--

---@type BP_ActionChar_C
local M = UnLua.Class()

local KismetSystemLibrary = UE.UKismetSystemLibrary

--- 角色类
local BP_ActionCharClass = UE.UClass.Load("/Script/Engine.Blueprint'/ActionPlatformer/Blueprints/Characters/BP_ActionChar.BP_ActionChar_C'")

--- 投掷武器类
local SwordClass = UE.UClass.Load("/ActionPlatformer/Blueprints/Weapon/BP_Sword.BP_Sword_C")
local GMClass = UE.UClass.Load("/ActionPlatformer/Blueprints/GM_Action.GM_Action_C")

-- 一个类似于蓝图FlipFlop的功能
local BoolState = { isOn = false }
function SetBoolState()
    BoolState.isOn = not BoolState.isOn
    return BoolState.isOn
end

-- function M:Initialize(Initializer)
-- end

function M:UserConstructionScript()
    self.World = self:GetWorld()
end

function M:ReceiveBeginPlay()
    self:InitializeTimer()
    self:InitializeValue()
    self:InitializingBinding()
    self:SetHitBoxCollision(false)
end

--- 初始化值
function M:InitializeValue()
    -- 当前血量初始化
    self.CurrentHealth = self.MaxHealth
    -- 图像的初始位置
    self.SpriteBaseLocation = self.Sprite.RelativeLocation
    -- 玩家控制器获取
    self.PlayerController = UE.UGameplayStatics.GetPlayerController(self, 0)
    self.GameMode = UE.UGameplayStatics.GetGameMode(self):Cast(GMClass)
    --- 投掷出去的武器数组初始化
    if self.PlayerController then
        for i = 1, self.MaxSword do
            self.ThrowSword:Add(nil)
        end
    end
end

--- 初始化每个实例的定时器
--- 在这个类有N个子类的情况下，还都用到了延迟就需要独立的定时器
function M:InitializeTimer()
    -- 打击感的定时器
    self.HitStopTime = UE.FTimerHandle()
    -- 抖动定时器
    self.ShakeTime = UE.FTimerHandle()
end

--- 初始化绑定相关
function M:InitializingBinding()
    self.HitBox.OnComponentBeginOverlap:Add(self, self.OnComponentBeginOverlap_HitBox)
end

-- function M:ReceiveEndPlay()
-- end

-- function M:ReceiveTick(DeltaSeconds)
-- end

-- function M:ReceiveAnyDamage(Damage, DamageType, InstigatedBy, DamageCauser)
-- end

-- function M:ReceiveActorBeginOverlap(OtherActor)
-- end

-- function M:ReceiveActorEndOverlap(OtherActor)
-- end

--- 碰撞box开始重叠
function M:OnComponentBeginOverlap_HitBox(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, FromSweep, SweepResult)
    if OtherActor ~= self then
        local HitActor = self         -- 也可以直接local HitActor = OtherActor:Cast(BP_ActionCharClass) 不过这样就不会提示函数了（这里的self等于一个类，类似于 actor = UE.AActor）
        HitActor = OtherActor:Cast(BP_ActionCharClass)
        if HitActor.Faction ~= self.Faction then
            HitActor:ReceiveAttack(10)
            HitActor:HitStopBeing(0.25)
            self:HitStopBeing(0.15)
            -- 处理攻击手柄抖动
            local HandleAttackShakes = UE.UObject.Load("/ActionPlatformer/Input/FFE_HandleAttackShakes.FFE_HandleAttackShakes")
            self:HandleShakes(HandleAttackShakes)
        end
    end
end

--- 追踪平台
function M:TraceForPlatforms()

    if self.CharacterMovement:IsFalling() and self.CanTestPlatform == true then
        local StartLocation = self.FootPosition:K2_GetComponentLocation()
        local EndLocation = StartLocation - UE.FVector(0, 0, 10)

        local HitResult = UE.FHitResult()
        local Hit = KismetSystemLibrary.BoxTraceSingleForObjects(
                self,
                EndLocation,
                EndLocation,
                UE.FVector(32, 32, 10),
                UE.FRotator(),
                self.PlatformsCollisionType,
                false,
                UE.TArray(UE.AActor),
                UE.EDrawDebugTrace.None,
                HitResult,
                true,
                UE.FLinearColor(1, 0, 0, 1), UE.FLinearColor(0, 1, 0, 1), 0.1
        )

        if Hit and self:GetVelocity().Z < 0 then
            --- 设置武器的碰撞
            local Sword = HitResult.HitObjectHandle.Actor:Cast(SwordClass)
            if Sword then
                Sword:SetPawnCollisionBlock()
            end
            self:SetCapsuleBlock()
        else
            self:SetCapsuleOverlap()
        end
    end

end

-- 设置胶囊体平台碰撞开启
function M:SetCapsuleBlock()
    self.CapsuleComponent:SetCollisionResponseToChannel(UE.ECollisionChannel.OneWayPlatform, UE.ECollisionResponse.ECR_Block)
end

-- 设置胶囊体平台重叠开启
function M:SetCapsuleOverlap()
    self.CapsuleComponent:SetCollisionResponseToChannel(UE.ECollisionChannel.OneWayPlatform, UE.ECollisionResponse.ECR_Overlap)
end

--- 设置命中盒子碰撞
function M:SetHitBoxCollision(SetActive)
    if SetActive then
        self.HitBox:SetCollisionEnabled(UE.ECollisionEnabled.QueryOnly)
    else
        self.HitBox:SetCollisionEnabled(UE.ECollisionEnabled.NoCollision)
    end
end

--- 受到攻击
function M:ReceiveAttack(Damage)
    if self.IsDefeated == true then
        return
    end
    if self.HitSound then
        UE.UGameplayStatics.PlaySoundAtLocation(self, self.HitSound, self:K2_GetActorLocation())
    end

    self.CurrentHealth = self.CurrentHealth - Damage

    self:GetAnimInstance():StopAllAnimationOverrides() -- 停止动画

    if self.CurrentHealth <= 0 then
        self.IsDefeated = true
        self:GetAnimInstance():JumpToNode("JumpDefeated")
        self.CapsuleComponent:SetCollisionEnabled(UE.ECollisionEnabled.NoCollision)
        self:ClearStun()

        self.AIPerceptionStimuliSource:UnregisterFromPerceptionSystem()
        self.Defeated:Broadcast() --- 广播被击败
    else
        self.IsStunned = true
        self.StunStateChanged:Broadcast(self.IsStunned) -- 广播受击

        self:GetAnimInstance():JumpToNode("JumpStunned")
        KismetSystemLibrary.K2_SetTimerDelegate({ self, self.ClearStun }, self.StunLength, false)

        self.ShakeTime = KismetSystemLibrary.K2_SetTimerDelegate({ self, self.ShakeSprite }, 0.05, true)
    end
end

--- 清除受击
function M:ClearStun()
    self.IsStunned = false
    self.StunStateChanged:Broadcast(self.IsStunned) -- 广播受击
    KismetSystemLibrary.K2_ClearAndInvalidateTimerHandle(self, self.ShakeTime)
    self.Sprite:K2_SetRelativeLocation(self.SpriteBaseLocation, false, nil, false)
    -- print("受击清除")
end

--- 可以行动
function M:CanAct()
    if not self.IsAttacking and not self.IsDefeated and not self.IsStunned then
        return true
    else
        return false
    end
end

--- 晃动图像
function M:ShakeSprite()
    local NewLocation = UE.FVector()
    local Negitive = SetBoolState() and 1 or -1

    local X = self.SpriteBaseLocation.X + self.SpriteShakeDistance * Negitive

    NewLocation:Set(X, self.SpriteBaseLocation.Y, self.SpriteBaseLocation.Z)
    self.Sprite:K2_SetRelativeLocation(NewLocation, false, nil, false)
end

--- 被击中暂停几毫秒(一种卡肉感)
function M:HitStopBeing(Duration)
    self.CustomTimeDilation = 0.1
    KismetSystemLibrary.K2_ClearAndInvalidateTimerHandle(self, self.HitStopTime)
    self.HitStopTime = KismetSystemLibrary.K2_SetTimerDelegate({ self, self.EndPause }, Duration, false)
end

--- 卡肉感结束
function M:EndPause()
    self.CustomTimeDilation = 1
    -- print(self:GetName() .. " --> " .. "卡肉感结束！！！")
end

--- 攻击
function M:Attack()
    self.IsAttacking = true
    local TempAttackAnim = self.AttackSequence[self.CurrentAttackAnim]
    self.CurrentAttackAnim = self.CurrentAttackAnim + 1
    if self.CurrentAttackAnim > self.AttackSequence:Num() then
        self.CurrentAttackAnim = 1
    end
    -- 创建一个动画播放动作，用于播放攻击动画，并设置回调，记得手动激活
    local Action = UE.UPaperZDPlaySlotOverrideAction.PlayAnimationOverrideWithCallbacks(
            self:GetAnimInstance(), -- 获取当前实例的动画实例
            TempAttackAnim, -- 指定要播放的攻击动画序列
            self, -- 获取当前实例所在的世界上下文
            "DefaultSlot" -- 动画播放的插槽名称
    )

    -- 绑定动画完成时的回调函数
    Action.OnCompleted:Add(self, self.AttackOnCompleted)

    -- 绑定动画被取消时的回调函数
    Action.OnCancelled:Add(self, self.AttackOnCancelled)

    -- 激活动画播放动作，开始播放攻击动画
    Action:Activate()
end

--- 攻击播放完成
function M:AttackOnCompleted()
    self.IsAttacking = false
    print("The attack animation is finished！(攻击动画播放完成)")
end

--- 攻击播放打断
function M:AttackOnCancelled()
    self.IsAttacking = false
    print("Animation cancelled!(攻击被打断了)")
end

--- 投掷
function M:Throw()
    self.IsAttacking = true
    -- 创建一个动画播放动作，用于播放攻击动画，并设置回调，记得手动激活
    local Action = UE.UPaperZDPlaySlotOverrideAction.PlayAnimationOverrideWithCallbacks(
            self:GetAnimInstance(), -- 获取当前实例的动画实例
            self.ThrowSequence, -- 指定要播放的投掷动画序列
            self, -- 获取当前实例所在的世界上下文
            "DefaultSlot" -- 动画播放的插槽名称
    )

    -- 绑定动画完成时的回调函数
    Action.OnCompleted:Add(self, self.ThrowOnCompleted)

    -- 绑定动画被取消时的回调函数
    Action.OnCancelled:Add(self, self.ThrowOnCancelled)

    -- 激活动画播放动作，开始播放攻击动画
    Action:Activate()
end

--- 投掷播放完成
function M:ThrowOnCompleted()
    self.IsAttacking = false
    print("The Throw animation is finished！(投掷动画播放完成)")
end

--- 投掷播放打断
function M:ThrowOnCancelled()
    self.IsAttacking = false
    print("Animation cancelled!(投掷被打断了)")
end

--- 生成投掷剑
function M:SpawnSword()
    if not SwordClass then
        return
    end
    local Transform = self.ThrowPos:K2_GetComponentToWorld()
    if self.World then

        if self.ThrowSword[self.CurrentSword] then
            self.ThrowSword[self.CurrentSword]:K2_DestroyActor()
        end
        self.ThrowSword[self.CurrentSword] = self.World:SpawnActor(SwordClass, Transform, UE.ESpawnActorCollisionHandlingMethod.AlwaysSpawn, self, self.Instigator)

        if self.ThrowSword[self.CurrentSword] then
            self.CurrentSword = self.CurrentSword + 1

            if self.CurrentSword > self.MaxSword then
                self.CurrentSword = 1
            end
        end
        --print(tostring(Sword:GetName()))
    end
end

--- 手柄抖动
---@param ForceFeedbackEffect UForceFeedbackEffect @力反馈资产
function M:HandleShakes(ForceFeedbackEffect)
    if self.PlayerController then
        if ForceFeedbackEffect then
            self.PlayerController:K2_ClientPlayForceFeedback(ForceFeedbackEffect, "HandleShakes", false, true, false)
            -- print("手柄可以震动" .. " - " .. ForceFeedbackEffect:GetName())
        end
    end
end

--- 获得能力
---@param AbilityType EAbility
function M:GainAbility(AbilityType)
    print(self:AbilityName(AbilityType))
    self.UnLockedAbilities:AddUnique(AbilityType)

    if AbilityType == UE.EAbility.EA_DoubleJump then
        self.JumpMaxCount = 2 --- 双倍跳跃能力
        return
    end

end

return M
